package Groep3;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Server {
	private BufferedReader inFromClient;
	private DataOutputStream outToClient ;
	private Socket connectionSocket;
	private ServerSocket welcomeSocket;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try
		{
			new Server();
		}
		catch( Exception e){

			System.out.println(e);

		}
	}
	
	public Server()
	{
		try
		{
			welcomeSocket = new ServerSocket(6789);
			System.out.println("server started");
		}
		catch(IOException e)
		{
			System.out.println("Kan server niet starten");
			return;
		}
		
		while( true )
		{
			try
			{
				handleConnections();
			}
			catch(IOException e)
			{
				// Gooi de socket dicht zodat we het nog een keer kunnen proberen
				try
				{
					if ( !connectionSocket.isClosed() )
						connectionSocket.close();
				}
				catch(IOException ee){}
				
				System.out.println(e.getMessage());
			}
			catch(Exception e)
			{
				System.out.println("Fatale error: " + e);
				System.exit(0);
			}finally{
			}
			}
		}
		
	
	public void handleConnections() throws IOException{ 
	
		
		try{	        
		connectionSocket = welcomeSocket.accept();
		inFromClient = new BufferedReader( new InputStreamReader( connectionSocket.getInputStream() ));
		outToClient = new DataOutputStream( connectionSocket.getOutputStream() );
		String clientSentence;
			
			while(connectionSocket.isConnected() == true){
		
			int lineNumber = 0;
			boolean bIsContent = false;
			int contentLength = 0;
			String data = "";
			
			String type = null, path = null;
		

				clientSentence = inFromClient.readLine();
			
				
				if ( clientSentence == null )
					clientSentence = "";
				
				lineNumber++;
				
				System.out.println("got: "+clientSentence);
				while(true){
				if ( lineNumber == 1)
				{
					if ( clientSentence.length() < 5 )
					{
						System.out.println("400 Bad Request");
						
						badRequest();
					
						return;
					}
						
					if ( clientSentence.substring(0, 3).equals("GET") )
					{
						type = "GET";
					
					}
					else if (clientSentence.substring(0, 4).equals("POST") )
					{
						type = "POST";
				
					}
					else if (clientSentence.substring(0, 4).equals("HEAD") )
					{
						type = "HEAD";
				
					}
					else
					{
						System.out.println("400 Bad Request");
						
						badRequest();
				
						return;
					}
					
					String[] splittedLine = clientSentence.split(" ");
					path = splittedLine[1];
				}
				else
				{
					if ( bIsContent )
					{	
						data += clientSentence;
						System.out.println(data);
						
						// Als data lengte gelijk of meer is dan Content-Length header
						// betekent dat we alles van de request binnen hebben
						if ( data.length() >= contentLength )
							break;
					}
					else
					{		
						if ( clientSentence.isEmpty() )
						{
							if ( type.equals("POST"))
							{
								// Na de eerste lege regel begint de data
								bIsContent = true;
								continue;
							}
							else
							{
								// Lege regel = einde request
								break;
							}
						}
						else
						{
							// Other headers here
							String[] splittedLine = clientSentence.split(":");
							if ( splittedLine.length > 0 && splittedLine[0].equals("Content-Length") )
							{
								contentLength = Integer.parseInt( clientSentence.substring(splittedLine[0].length() + 2 ) );
							}
						}
					}
				}
			
			
			// Als de path eindigt met /, dan gooi er index.html erachter
			if ( path.charAt( path.length() - 1 ) == '/' )
				path += "index.html";
			
			// Stuur een 307 Redirect als de path redirect.txt is
			if ( path.equals("redirect.txt") )
			{
		
				outToClient.writeBytes( "HTTP/1.1 307 Temporary Redirect\r\n" );
				
				// Date: Tue, 02 Oct 2007 13:46:12 GMT
				DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", Locale.ENGLISH);
				df.setTimeZone(TimeZone.getTimeZone("GMT"));
		        Date now = new Date();
		        outToClient.writeBytes( "Date: " + df.format(now) + " GMT\r\n");
		        
				outToClient.writeBytes( "Host: Frederike's server\r\n" );
				outToClient.writeBytes( "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n");
				outToClient.writeBytes( "Connection: close\r\n");
				outToClient.writeBytes( "Location: /redirect2.txt\r\n");
				outToClient.writeBytes( "\r\n" );
				
				System.out.println("307 Temporary Redirect");
				connectionSocket.close();
				
				
			}
			else if ( path.equals("/error.txt") )
			{
				serverError();
				
				System.out.println("500 Internal Server Error");
			}
			else
			{
			
				File file = new File("data/" + path);
				if ( file.canRead() )
				{
					System.out.println("200 OK " + path);
					
					outToClient.writeBytes( "HTTP/1.1 200 OK\r\n" );
					
					
					// Date: Tue, 02 Oct 2007 13:46:12 GMT
					DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", Locale.ENGLISH);
					df.setTimeZone(TimeZone.getTimeZone("GMT"));
			        Date now = new Date();
			        outToClient.writeBytes( "Date: " + df.format(now) + " GMT\r\n");
			       
					outToClient.writeBytes( "Host: Frederike's server\r\n" );
					outToClient.writeBytes( "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n");
					outToClient.writeBytes( "Content-Type: " + FileToMimetype(file) + "\r\n" );
					outToClient.writeBytes( "Content-Length: " + file.length() + "\r\n" );
					outToClient.writeBytes( "Connection: close\r\n");
					outToClient.writeBytes( "\r\n" );
					
					if ( !type.equals("HEAD") )
					{
						FileInputStream fileReader = new FileInputStream(file);
						byte[] buff = new byte[5000];
						
						while( fileReader.read(buff) != -1)
						{
							outToClient.write(buff);
						}
						fileReader.close();
					}
				}
				else
				{
					System.out.println("404 Not Found: " + path);
					
				
					notFound();
					return;
				}
				
				System.out.println( type + ": " + path);
				if ( type.equals( "POST") )
				{
					System.out.println("Data: " + data);
				}
				connectionSocket.close();
				}
				}
		}
		}catch(SocketException e1){
			
		}catch(IOException e){

			// Gooi de socket dicht zodat we het nog een keer kunnen proberen
			try
			{
				if ( !connectionSocket.isClosed() )
					connectionSocket.close();
			}
			catch(IOException ee){}
			e.getMessage();
		}
	
	}
	
	public void badRequest(){
		
		String msg = "Onbekende opdracht";
		
		try {
			outToClient.writeBytes("HTTP/1.1 400 Bad Request"  +    "\n");
		
		
		// Date: Tue, 02 Oct 2007 13:46:12 GMT
		DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", Locale.ENGLISH);
		df.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date now = new Date();
			outToClient.writeBytes( "Date: " + df.format(now) + " GMT\r\n");
			outToClient.writeBytes("Content-Type: text/plain\r\n");
			outToClient.writeBytes("Connection: close\r\n");
			outToClient.writeBytes("Content-Length:  " + msg.length() + "\r\n");
			outToClient.writeBytes("\r\n");
			outToClient.writeBytes( msg);
			connectionSocket.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
        
		
		//The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications. 
	}
	
	public void notFound(){
		String msg = "Die pagina bestaat niet!";
		
		try{
		outToClient.writeBytes("HTTP/1.1 404 Not Found\r\n");
		// Date: Tue, 02 Oct 2007 13:46:12 GMT
		DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", Locale.ENGLISH);
		df.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date now = new Date();
        outToClient.writeBytes( "Date: " + df.format(now) + " GMT\r\n");
		        
        outToClient.writeBytes("Content-Type: text/plain\r\n");
        outToClient.writeBytes("Connection: close\r\n");
        outToClient.writeBytes("Content-Length:  " + msg.length() + "\r\n");
        outToClient.writeBytes("\r\n");
        outToClient.writeBytes(msg);
		connectionSocket.close();
		//The server has not found anything matching the Request-URI. No indication is given of whether the condition is temporary or permanent. The 410 (Gone) status code SHOULD be used if the server knows, through some internally configurable mechanism, that an old resource is permanently unavailable and has no forwarding address. This status code is commonly used when the server does not wish to reveal exactly why the request has been refused, or when no other response is applicable. 
	
		}catch(SocketException ee){
			
		}catch( IOException e){
			e.printStackTrace();
		}
	}
		
	
	public void serverError() throws IOException{
		String msg = "Server error opgetreden";
		outToClient.writeBytes("HTTP/1.1 500 Internal Server Error\r\n");
				
		// Date: Tue, 02 Oct 2007 13:46:12 GMT
		DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", Locale.ENGLISH);
		df.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date now = new Date();
        outToClient.writeBytes( "Date: " + df.format(now) + " GMT\r\n");
		        
        outToClient.writeBytes("Content-Type: text/plain\r\n");
        outToClient.writeBytes("Connection: close\r\n");
        outToClient.writeBytes("Content-Length:  " + msg.length() + "\r\n");
        outToClient.writeBytes("\r\n");
        outToClient.writeBytes(msg);
		
		connectionSocket.close();
//		 Response status codes beginning with the digit "5" indicate cases in which the server is aware that it has erred or is incapable of performing the request. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. User agents SHOULD display any included entity to the user. These response codes are applicable to any request method.
//		 10.5.1 500 Internal Server Error
//
//		 The server encountered an unexpected condition which prevented it from fulfilling the request. 
	}
	
	

	private String FileToMimetype(File file) {
			String[] filename = file.getName().split("\\.");
			if ( filename.length > 1 )
			{
				String extension = filename[filename.length-1];
				if ( extension.equals("gif") )
					return "image/gif";
				
				else if ( extension.equals("png") )
					return "image/png";
				
				else if ( extension.equals("txt") )
					return "text/plain";
				
				else if ( extension.equals("html") || extension.equals("htm") )
					return "text/html";
			}
			
			return "text/plain";
		
	}

}
